package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"recorder/controller"
	"recorder/internal"
	"recorder/log"
	"recorder/worker"
	"strings"
)

var (
	optVerbose  bool
	optHome     string
	optListen   string
	optConnect  string
	optKeepDays int
	optId       string
	optOutDir   string

	optNotMergeWhenOneFile bool
)

var config internal.Config

func parseOptions() {
	flag.BoolVar(&optVerbose, "v", false, "output detail log")
	flag.StringVar(&optHome, "home", "", "recorder home path")
	flag.StringVar(&optListen, "listen", "0.0.0.0:10000", "controller listen address")
	flag.StringVar(&optConnect, "connect", "", "controller address to connect")
	flag.IntVar(&optKeepDays, "keep", 30, "how many days old files are kept")
	flag.StringVar(&optId, "id", defaultId(), "instance id, used in log and history")
	flag.StringVar(&optOutDir, "out-dir", "", "output dir")
	flag.BoolVar(&optNotMergeWhenOneFile, "notMergeWhenOneFile", true, "when only record files, do not merger. keep it.")
	flag.Parse()
	switch {
	case len(optHome) == 0:
		log.Error("must specify -home option")
		flag.PrintDefaults()
		return
	case len(optConnect) == 0 && len(optListen) == 0:
		log.Error("must specify -listen or -connect option")
		flag.PrintDefaults()
		return
	}

	config = internal.Config{
		Id:                  optId,
		JobPath:             filepath.Join(optHome, "job"),
		TlvPath:             filepath.Join(optHome, "tlv"),
		OutPath:             filepath.Join(optHome, "out"),
		BakPath:             filepath.Join(optHome, "bak"),
		LogPath:             filepath.Join(optHome, "log"),
		TmpPath:             filepath.Join(optHome, "tmp"),
		HstPath:             filepath.Join(optHome, "history.db"),
		KeepDays:            optKeepDays,
		NotMergeWhenOneFile: optNotMergeWhenOneFile,
	}

	if optOutDir != "" {
		config.OutPath = optOutDir
	}
}

func makeRuntimeDirs() {
	if _, err := os.Stat(config.JobPath); err != nil {
		os.MkdirAll(config.JobPath, os.ModePerm)
	}
	if _, err := os.Stat(config.TlvPath); err != nil {
		os.MkdirAll(config.TlvPath, os.ModePerm)
	}
	if _, err := os.Stat(config.BakPath); err != nil {
		os.MkdirAll(config.BakPath, os.ModePerm)
	}
	if _, err := os.Stat(config.OutPath); err != nil {
		os.MkdirAll(config.OutPath, os.ModePerm)
	}
	if _, err := os.Stat(config.LogPath); err != nil {
		os.MkdirAll(config.LogPath, os.ModePerm)
	}
	if _, err := os.Stat(config.TmpPath); err != nil {
		os.MkdirAll(config.TmpPath, os.ModePerm)
	}
}

func main() {
	parseOptions()
	makeRuntimeDirs()
	log.Init(optVerbose, filepath.Join(optHome, "log"), optId)
	log.Info("config %+v", config)

	userStop := make(chan os.Signal, 1)
	exit := make(chan struct{})
	signal.Notify(userStop, os.Interrupt)

	if len(optConnect) != 0 {
		config.Addr = optConnect
		w := worker.New(&config)
		go func() {
			w.Run()
			close(exit)
		}()
		select {
		case <-userStop:
			w.Close()
		case <-exit:
			return
		}
	} else {
		config.Addr = optListen
		b := controller.New(&config)
		go func() {
			b.Run()
			close(exit)
		}()
		select {
		case <-userStop:
			b.Close()
		case <-exit:
			return
		}
	}
}

func defaultId() string {
	host := "unknown"
	if h, e := os.Hostname(); e == nil {
		host = h
	}
	prog := filepath.Base(os.Args[0])
	pid := fmt.Sprint(os.Getpid())
	if _, e := os.Stat("/.dockerenv"); e == nil {
		if f, e := os.Open("/proc/self/cgroup"); e == nil {
			if s, e := bufio.NewReader(f).ReadString('\n'); e == nil {
				parts := strings.Split(s, "/")
				pid = parts[len(parts)-1]
				if len(pid) > 12 {
					pid = pid[:12]
				}
			}
			f.Close()
		}
	}
	return fmt.Sprint(host, "-", prog, "-", pid)
}
